package com.hmjk.health.background;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import org.json.JSONException;
import org.json.JSONObject;

import com.hmjk.health.HealthApp;
import com.hmjk.health.R;
import com.hmjk.health.SplashActivity;
import com.hmjk.health.setting.SettingsConstants;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.IBinder;
import android.provider.Settings.Secure;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

public class MqttService extends Service implements MqttCallback{
	private static final String TAG = MqttService.class.getSimpleName();
	private static final boolean DEBUG = true;
	private final long ACCEPTED_TIME_DURATION = 1000 * 60 * 60 * 48; //48 hours
	public static final String START_MQTT_ACTION = "com.hmjk.mqtt.start";
	public static final String STOP_MQTT_ACTION = "com.hmjk.mqtt.stop";
	
	//private String HOST = "tcp://139.196.5.71:1883";
	private String HOST = "tcp://app.hmjk.hmyd.com:1883";
	private String TOPIC_TODO = "%1$s/#";
	private String TOPIC_TEST = "topic/test";
	private int QOS = 2;
	private String USERNAME = "test";
	private String PASSWORD = "password";
	
	private final String CMD_START = "CMD_START";
	private final String CMD_STOP = "CMD_STOP";
	private final String CMD_QUIT = "CMD_QUIT";
	
	private HealthApp mApp;
	private MqttConnectOptions conOpt;
	private MqttClient mClient;
	private String mClientID = null;
	private String mSubscribedUid = null;
	//private boolean clearOld = true;
	private NotificationManager	mNotifMan;
	
	private MqttCmdThread mThread = null;
	private final int MAX_QUEUE_SIZE = 20;
	private BlockingQueue<String> mCmdQueue = new LinkedBlockingQueue<String>();
	private ScheduledExecutorService mScheduler;
	
	private void logd(String info){
		if(DEBUG)
			Log.d(TAG, info);
	}
	
	private void loge(String info){
		Log.e(TAG, info);
	}
	
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return null;
	}

	private boolean doSubscribe = false;
	@Override  
    public void onCreate() {  
    	logd("onCreate : + this="+this);
        super.onCreate();  
        mApp = (HealthApp)getApplication();
        mNotifMan = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        init();
        doSubscribe = false;
        mThread = new MqttCmdThread();
        mThread.start();
        
        startReconnectScheduler();
	}
	
	private void stopReconnectScheduler(){
		logd("stopReconnectScheduler");
		if((mScheduler != null) && (false == mScheduler.isShutdown())){
			logd("stopReconnectScheduler stop scheduler");
			mScheduler.shutdown();
			mScheduler = null;
		}
	}
	
	private void startReconnectScheduler() {
		logd("startReconnectScheduler");
		if((mScheduler == null) || mScheduler.isShutdown()){
			logd("startReconnectScheduler start scheduler");
			mScheduler = Executors.newSingleThreadScheduledExecutor();
			mScheduler.scheduleAtFixedRate(new Runnable() {
	 
	            @Override
	            public void run() {
	            	logd("scheduled check");
					if(false == mApp.checkNetwork()){
						logd("schedule no network");
			    		return;
					}
					
	                if(!mClient.isConnected()) {
	                	logd("schedule send CMD_START");
	                	offerCMD(CMD_START);
	                }else{
	                	logd("schedule already connected");
	                }
	            }
	        }, 0 * 1000, 30 * 1000, TimeUnit.MILLISECONDS);
		}
    }
	
	@Override  
    public void onStart(Intent intent, int startId) {  
        logd("onStart : + this="+this);  
        super.onStart(intent, startId);  
    }  
	
	@Override  
    public void onDestroy() {  
        logd("onDestroy : + this="+this);
        stopReconnectScheduler();
        
        if(mThread != null){
        	offerCMD(CMD_QUIT);
        	mThread.stopRunning();
        	mThread = null;
        }
        
        if((mClient != null) && mClient.isConnected()){
			try {
				mClient.disconnect();
			} catch (MqttException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
        mClient = null;
		
        Intent si = new Intent(START_MQTT_ACTION);
		startService(si);
        super.onDestroy();  
    } 
	
	@Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
    	if(intent == null){
    		logd("onStartCommand auto + this="+this + " flags="+flags+" startId="+startId);
    		String uid = mApp.getUID();
    		if((uid != null) && (false == uid.trim().isEmpty())){
    			if(false == mApp.checkNetwork()){
					logd("onStartCommand no network");
				}else{
    			offerCMD(CMD_START);
				}
			}else{
				offerCMD(CMD_STOP);
			}
    	}else{
    		String action = intent.getAction();
    		logd("onStartCommand + this="+this + " action="+action+" startId="+startId);
    		if(START_MQTT_ACTION.equals(action)){
    			String uid = mApp.getUID();
    			if((uid != null) && (false == uid.trim().isEmpty())){
    				if(false == mApp.checkNetwork()){
    					logd("onStartCommand no network");
    				}else{
    				offerCMD(CMD_START);
    				}
    			}else{
    				offerCMD(CMD_STOP);
    			}
    		}else if(STOP_MQTT_ACTION.equals(action)){
    			offerCMD(CMD_STOP);
    		}
    	}
    	return START_STICKY;
	}
	
	private boolean offerCMD(String cmd){
    	int size = mCmdQueue.size();
    	while(size >= MAX_QUEUE_SIZE){
    		String dis = mCmdQueue.poll();
    		logd("discard cmd:" + dis);
    		size = mCmdQueue.size();
    	}
    	boolean ret = mCmdQueue.offer(cmd);
    	logd("offerREQ:" + cmd + " ret="+ret);
    	
    	return ret;
    }
	
	class MqttCmdThread extends Thread{
    	private boolean stop = false;
    	
    	public void stopRunning(){
    		stop = true;
    	}
    	
    	@Override
    	public void run(){
    		while(!stop){
    			try {
					String cmd = mCmdQueue.take();
					if(CMD_QUIT.equals(cmd)){
						loge("do " + cmd);
						mCmdQueue.clear();
						stop = true;
						break;
					}else if(CMD_STOP.equals(cmd)){
						loge("do " + cmd);
						stopReconnectScheduler();
						try {
							if(mClient.isConnected())
								mClient.disconnect();
						} catch (MqttException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}else if(CMD_START.equals(cmd)){
						String uid = mApp.getUID();
						if((uid == null) || uid.trim().isEmpty()){
							loge("do " + cmd + "no uid");
							continue;
						}
						
						logd("do " + cmd + " uid = " + uid + " mSubscribedUid = "+mSubscribedUid);
						if(connect()){
							try {
				    			if((mSubscribedUid != null) && (mSubscribedUid.equals(uid) == false)){
				    			//if((mSubscribedUid != null) && (clearOld || (mSubscribedUid.equals(uid) == false))){
				    				String[] untopics = new String[1];
									untopics[0] = String.format(TOPIC_TODO, mSubscribedUid);
									logd("uid changed||clearold==true, unsubscribe old topic");// clearOld="+clearOld);
									logd("unsubscribe topic = " + untopics[0]);
									mClient.unsubscribe(untopics);
									
									mSubscribedUid = null;
									saveSubscribedUid();
									//clearOld = false;
				    			}
				    			if((mSubscribedUid == null) || doSubscribe){
				    				logd("subscribe uid = " + uid);
				    				String[] topics = new String[1];
				    				topics[0] = String.format(TOPIC_TODO, uid);
				    				logd("subscribe topic = " + topics[0]);
				    				//topics[1] = TOPIC_TEST;
				    				int[] qoss = new int[1];
				    				qoss[0] = QOS;
				    				//qoss[1] = QOS;
				    				if(doSubscribe){
				    					logd("unsubscribe:" + topics[0]);
				    					mClient.unsubscribe(topics);
				    				}
				    				mClient.subscribe(topics, qoss);
				    				mSubscribedUid = uid;
				    				saveSubscribedUid();
				    				doSubscribe = false;
				    				logd("subscribed!");
				    			}else{
				    				logd("subscribed already!");
				    			}
							} catch (MqttException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    		}
    		logd("MqttCmdThread quit");
    	}
	}
	
	private boolean connect(){
		if(mClient.isConnected()){
			logd("connect:connected already!");
			return true;
		}
			
		try {
			mClient.connect(conOpt);
			logd("connect:connected!");
			startReconnectScheduler();
			return true;
		} catch (MqttSecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MqttException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		logd("connect:failed!");
		startReconnectScheduler();
		return false;
	}
	
	private void getCliendID(){
		SharedPreferences pref = getSharedPreferences(SettingsConstants.GENERAL_SETTING, MODE_PRIVATE);
		mClientID = pref.getString(SettingsConstants.KEY_MQTT_ID, "");
        if(mClientID.trim().isEmpty()){
        	mClientID = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
        	
        	Editor editor = pref.edit();
        	editor.putString(SettingsConstants.KEY_MQTT_ID, mClientID);
        	editor.commit();
        }
	}
	
	private void saveSubscribedUid(){
		SharedPreferences pref = getSharedPreferences(SettingsConstants.GENERAL_SETTING, MODE_PRIVATE);
		Editor editor = pref.edit();
    	editor.putString(SettingsConstants.KEY_MQTT_SUBSCRIBED_UID, mSubscribedUid);
    	editor.commit();
	}
	
	private void init(){
    	try {
    		getCliendID();
    		
    		SharedPreferences pref = getSharedPreferences(SettingsConstants.GENERAL_SETTING, MODE_PRIVATE);
    		mSubscribedUid = pref.getString(SettingsConstants.KEY_MQTT_SUBSCRIBED_UID, "");
    		if(mSubscribedUid != null){
    			if(mSubscribedUid.trim().isEmpty())
    				mSubscribedUid = null;
    		}
    		/*if(mSubscribedUid != null){
    			clearOld = true;
    		}else{
    			clearOld = false;
    		}*/
    		
    		String path = mApp.getMqttPath();
        	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(path);
        	mClient = new MqttClient(HOST, mClientID, dataStore);
        	conOpt = new MqttConnectOptions();
            conOpt.setCleanSession(false);
            conOpt.setUserName(USERNAME);
            conOpt.setPassword(PASSWORD.toCharArray());
            conOpt.setConnectionTimeout(10);
            conOpt.setKeepAliveInterval(20);
			
			mClient.setCallback(this);
			mClient.setTimeToWait(10 * 1000);
		} catch (MqttException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			mClient = null;
		}
	}

	@Override
	public void connectionLost(Throwable e) {
		logd("connectionLost" + e);
	}

	@Override
	public void deliveryComplete(IMqttDeliveryToken token) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void messageArrived(String topic, MqttMessage m) throws Exception {
		//topic = 29281efece4040a5aef5b990bf4efb8d/0401 msg = {"remberid":"29281efece4040a5aef5b990bf4efb8d","membercode":"0401","membermsg":"测试666"}
		if(mSubscribedUid == null){
			logd("Arrived:mSubscribedUid null");
			return;
		}
		if(topic.startsWith(mSubscribedUid)){
		//	String test = "用户";
			String msg = new String(m.getPayload(),"UTF_8");
			//String msg = new String(m.getPayload());
			logd("Arrived:my message:"+msg);
			try{
			JSONObject json = new JSONObject(msg);
			String rid = json.getString("remberid");
			String code = json.getString("membercode");
			String show = json.getString("membermsg");
			if(json.has("currenttime") == false)
				return;
			
			long time = json.getLong("currenttime");
			long curtime = System.currentTimeMillis();
			if((curtime - time) > ACCEPTED_TIME_DURATION){
				logd("Arrived:msg is too old");
				return;
			}
			
			mApp.getDataManager().setUserData(DataManager.F_TODO, null);
			mApp.getDataManager().refreshData(DataManager.F_TODO);
			if(mApp.getMQTTMsgOn()){
				showNotification("TODO",rid,show,time);
			}
			}catch(JSONException e){
				e.printStackTrace();
			}
			
		}else{
			logd("Arrived:whose message? topic = " + topic);
		}
	}
	
	private void showNotification(String type, String rid, String text, long time) {
		NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
		
		Intent i = new Intent(this,SplashActivity.class);
		/*add the followed two lines to resume the app same with previous statues*/
		i.setAction(Intent.ACTION_MAIN);
		i.addCategory(Intent.CATEGORY_LAUNCHER);
		/**/
		i.putExtra("from", "MQTT");
		i.putExtra("type", type);
		i.putExtra("param", rid);
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i,PendingIntent.FLAG_UPDATE_CURRENT);
		
		int method = Notification.DEFAULT_LIGHTS;
		//Notification.DEFAULT_VIBRATE    //添加默认震动提醒  需要 VIBRATE permission
		//Notification.DEFAULT_SOUND    // 添加默认声音提醒
		//Notification.DEFAULT_LIGHTS// 添加默认三色灯提醒
		//Notification.DEFAULT_ALL// 添加默认以上3种全部提醒
		if(mApp.getMQTTSoundOn()){
			method |= Notification.DEFAULT_SOUND;
		}
		if(mApp.getMQTTVibOn()){
			method |= Notification.DEFAULT_VIBRATE;
		}
		String title = getResources().getString(R.string.app_name);
	    mBuilder.setContentTitle(title).setContentText(text).setTicker(text).setWhen(time)
	    .setAutoCancel(true).setOngoing(false).setDefaults(method)
	    .setSmallIcon(R.drawable.app_logo)
        .setContentIntent(pendingIntent);  
    
	    mNotifMan.notify(rid,10,mBuilder.getNotification());
        
        
	}
}
